package view_server

import (
	"bigdevops/src/cache"
	"bigdevops/src/common"
	"bigdevops/src/config"
	"bigdevops/src/models"
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/go-playground/validator/v10"
	"go.uber.org/zap"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"strconv"
	"strings"
)

func getK8sAppList(c *gin.Context) {

	sc := c.MustGet(common.GIN_CTX_CONFIG_CONFIG).(*config.ServerConfig)

	currentPage, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
	pageSize, _ := strconv.Atoi(c.DefaultQuery("pageSize", "100"))

	offset := 0
	limit := 0
	limit = pageSize
	if currentPage > 1 {
		offset = (currentPage - 1) * limit
	}
	// 这里只能新增 不能去掉，因为是 多种资源共用的query
	searchUserID := c.DefaultQuery("UserID", "")

	searchUserIDInt, _ := strconv.Atoi(searchUserID)
	searchName := c.DefaultQuery("name", "")

	// 查询条件
	searchCluster := c.DefaultQuery("cluster", "")
	searchProjectId, _ := strconv.Atoi(c.DefaultQuery("projectId", ""))

	// 	数据库中拿到所有的menu列表

	var (
		objs []*models.K8sApp
		err  error
	)
	if searchCluster != "" && searchProjectId > 0 {
		objs, err = models.GetK8sAppByClusterAndProjectId(searchCluster, searchProjectId)
		if err != nil {
			sc.Logger.Error("去数据库中拿所有的k8s应用配置错误",
				zap.Error(err),
			)
			common.ReqBadFailWithMessage(fmt.Sprintf("去数据库中拿所有的k8s应用配置错误:%v", err.Error()), c)
			return
		}

	} else {
		objs, err = models.GetK8sAppAll()
		if err != nil {
			sc.Logger.Error("去数据库中拿所有的k8s应用配置错误",
				zap.Error(err),
			)
			common.ReqBadFailWithMessage(fmt.Sprintf("去数据库中拿所有的k8s应用配置错误:%v", err.Error()), c)
			return
		}

	}

	allIds := []int{}
	//// 遍历 role 准备menuIds 列表
	for _, obj := range objs {
		obj := obj

		// 在这里处理查询
		if searchName != "" && !strings.Contains(obj.Name, searchName) {
			continue
		}

		// 在这里处理查询
		if searchUserID != "" && int(obj.UserID) != searchUserIDInt {
			continue
		}
		allIds = append(allIds, int(obj.ID))

		//role.MenuIds = menuIds
	}

	if len(allIds) == 0 {
		common.OkWithDetailed(allIds, "ok", c)
		return
	}
	objs, err = models.GetK8sAppByIdsWithLimitOffset(allIds, limit, offset)
	if err != nil {
		sc.Logger.Error("limit-offset去数据库中拿所有的任务错误",
			zap.Error(err),
		)
		common.ReqBadFailWithMessage(fmt.Sprintf("limit-offset去数据库中拿所有的脚本模板错误:%v", err.Error()), c)
		return
	}
	for _, obj := range objs {
		obj := obj
		obj.FillFrontAllData()
	}

	resp := &ResponseResourceCommon{
		//Total: models.GetK8sAppCount(), 因为是带了查询条件的 所以 count 不能是all 而是 过滤后的total

		Total: len(allIds),
		Items: objs,
	}

	common.OkWithDetailed(resp, "ok", c)
}

func createK8sAppOne(c *gin.Context) {
	//kset *kubernetes.Clientset, podName string, containerName string, ns string string
	sc := c.MustGet(common.GIN_CTX_CONFIG_CONFIG).(*config.ServerConfig)
	var reqObj models.K8sApp
	err := c.ShouldBindJSON(&reqObj)
	if err != nil {
		sc.Logger.Error("解析createK8sAppOne配置请求失败", zap.Any("k8s配置", reqObj), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}

	// 在这里校验字段，是否必填，范围是否正确
	err = validate.Struct(reqObj)
	if err != nil {

		// 这里为什么要判断错误是否是 ValidationErrors
		if errors1, ok := err.(validator.ValidationErrors); ok {
			common.ReqBadFailWithWithDetailed(

				gin.H{
					"翻译前": err.Error(),
					"翻译后": errors1.Translate(trans),
				},
				"请求出错",
				c,
			)
			return
		}
		common.ReqBadFailWithMessage(err.Error(), c)
		return

	}

	// TODO 下面设置人和校验权限 在noauth验证的时候会报错，因为没有过jwt模块，你自己要注释一下
	// 设置人
	userName := c.MustGet(common.GIN_CTX_JWT_USER_NAME).(string)
	dbUser, err := models.GetUserByUserName(userName)
	if err != nil {
		sc.Logger.Error("通过token解析到的userName去数据库中找User失败",
			zap.Error(err),
		)
		common.ReqBadFailWithMessage(fmt.Sprintf("通过token解析到的userName去数据库中找User失败:%v", err.Error()), c)
		return
	}

	reqObj.UserID = dbUser.ID

	reqObj.FillDefaultData()

	err = reqObj.FillFrontAllData()
	if err != nil {
		msg := fmt.Sprintf("FillFrontAllData报错:%v", err.Error())
		sc.Logger.Error(msg)
		common.ReqBadFailWithMessage(msg, c)
		return
	}

	// 在创建节点前要校验权限
	pass, _ := streeNodeOpsAdminPermissionCheck(reqObj.StreeNodeObj, c)
	if !pass {
		sc.Logger.Error("服务树节点权限校验未通过", zap.Any("reqNode", reqObj.TreeNodeId), zap.Error(err))
		common.Req403WithWithMessage("服务树节点权限校验未通过", c)
		return

	}
	//  到这里

	// 判断ns是否在这里
	// 从cache中通过 集群的id 拿到集群的clientSet
	kc := c.MustGet(common.GIN_CTX_K8s_CACHE).(*cache.K8sClusterCache)
	ok := kc.JudgeClusterNamespace(reqObj.ProjectObj.Cluster, reqObj.Namespace)
	if !ok {
		msg := fmt.Sprintf("创建k8s app失败 这个集群:%v ns%v不存在", reqObj.ProjectObj.Cluster, reqObj.Namespace)
		sc.Logger.Error(msg, zap.Any("集群", ""), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}

	// 根据服务树校验权限
	// app的集群信息来自 project的
	reqObj.Cluster = reqObj.ProjectObj.Cluster

	// 生成svc并创建
	kSet := kc.GetClusterClientSetById(reqObj.ClusterObj.ID)
	if kSet == nil {
		msg := fmt.Sprintf("根据id找k8s集群kset缓存错误")
		sc.Logger.Error(msg, zap.Any("k8s集群配置", reqObj.ClusterObj.ID), zap.Error(err))
		common.FailWithMessage(msg, c)
		return
	}
	svc := reqObj.GenService()
	if svc != nil {
		err = createK8sInstanceOneApplyService(c, kSet, reqObj.ClusterObj, svc)
		if err != nil {

			msg := fmt.Sprintf("创建Service或更新错误:%v", err.Error())
			sc.Logger.Error(msg, zap.Any("k8s集群配置", reqObj.ClusterObj.ID), zap.Error(err))
			common.FailWithMessage(msg, c)
			return
		}
	}

	// 下面级联创建
	err = reqObj.CreateOne()
	if err != nil {
		sc.Logger.Error("新增k8s-实例数据库失败", zap.Any("k8s集群配置", reqObj), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}

	common.OkWithData(reqObj, c)

	//common.OkWithMessage("创建成功", c)
}

func updateK8sAppOne(c *gin.Context) {
	//kset *kubernetes.Clientset, podName string, containerName string, ns string string
	sc := c.MustGet(common.GIN_CTX_CONFIG_CONFIG).(*config.ServerConfig)
	var reqObj models.K8sApp
	err := c.ShouldBindJSON(&reqObj)
	if err != nil {
		sc.Logger.Error("解析createK8sAppOne配置请求失败", zap.Any("k8s配置", reqObj), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}
	// 在这里校验字段，是否必填，范围是否正确
	err = validate.Struct(reqObj)
	if err != nil {

		// 这里为什么要判断错误是否是 ValidationErrors
		if errors1, ok := err.(validator.ValidationErrors); ok {
			common.ReqBadFailWithWithDetailed(

				gin.H{
					"翻译前": err.Error(),
					"翻译后": errors1.Translate(trans),
				},
				"请求出错",
				c,
			)
			return
		}
		common.ReqBadFailWithMessage(err.Error(), c)
		return

	}

	reqObj.FillDefaultData()

	err = reqObj.FillFrontAllData()
	if err != nil {
		msg := fmt.Sprintf("FillFrontAllData报错:%v", err.Error())
		sc.Logger.Error(msg)
		common.ReqBadFailWithMessage(msg, c)
		return
	}

	// 在创建节点前要校验权限
	pass, _ := streeNodeOpsAdminPermissionCheck(reqObj.StreeNodeObj, c)
	if !pass {
		sc.Logger.Error("服务树节点权限校验未通过", zap.Any("reqNode", reqObj.TreeNodeId), zap.Error(err))
		common.Req403WithWithMessage("服务树节点权限校验未通过", c)
		return

	}
	//  到这里

	// 判断ns是否在这里
	// 从cache中通过 集群的id 拿到集群的clientSet
	kc := c.MustGet(common.GIN_CTX_K8s_CACHE).(*cache.K8sClusterCache)
	ok := kc.JudgeClusterNamespace(reqObj.ProjectObj.Cluster, reqObj.Namespace)
	if !ok {
		msg := fmt.Sprintf("创建k8s app失败 这个集群:%v ns%v不存在", reqObj.ProjectObj.Cluster, reqObj.Namespace)
		sc.Logger.Error(msg, zap.Any("集群", ""), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}

	searchCluster := reqObj.Cluster
	//searchName := reqObj.Name

	if searchCluster == "" {
		msg := "集群名称未传入"
		sc.Logger.Error(msg)
		common.ReqBadFailWithMessage(msg, c)
		return
	}

	dbObj, err := models.GetK8sClusterByName(searchCluster)
	if err != nil {
		sc.Logger.Error("根据name找k8s集群错误", zap.Any("k8s集群配置", searchCluster), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}
	// 从cache中通过 集群的id 拿到集群的clientSet
	kSet := kc.GetClusterClientSetById(dbObj.ID)
	if kSet == nil {
		msg := fmt.Sprintf("根据id找k8s集群kset缓存错误")
		sc.Logger.Error(msg, zap.Any("k8s集群配置", searchCluster), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}

	// 下面级联更新
	err = reqObj.UpdateOne()
	if err != nil {
		sc.Logger.Error("新增k8s-实例数据库失败", zap.Any("k8s集群配置", reqObj), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}

	// 更新了app ，如果下面有instance了，那么我应该 重新生成所有的Deployment update
	// 先查询数据库，不看k8s 有没有instance
	dbInstances, err := models.GetK8sInstanceByK8sAppId(int(reqObj.ID))
	if err != nil {

		msg := fmt.Sprintf("根据k8sAppId找k8s-instance数据库错误")
		sc.Logger.Error(msg, zap.Any("k8s集群配置", searchCluster), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}

	if len(dbInstances) == 0 {
		common.OkWithMessage("更新成功 没有instance ，无需update-Deployment", c)
		return
	}

	// 获取

	// 生成Deployment只走instance不要在app再维护一份
	// 遍历调用 instance的 GenDeployment 再生成, 去update
	// 先更新svc

	svc := reqObj.GenService()
	if svc != nil {
		err = createK8sInstanceOneApplyService(c, kSet, reqObj.ClusterObj, svc)
		if err != nil {

			msg := fmt.Sprintf("创建Service或更新错误:%v", err.Error())
			sc.Logger.Error(msg, zap.Any("k8s集群配置", reqObj.ClusterObj.ID), zap.Error(err))
			common.FailWithMessage(msg, c)
			return
		}
	}

	for _, dbInstance := range dbInstances {
		dbInstance := dbInstance
		err = dbInstance.FillFrontAllData()
		if err != nil {
			continue
		}
		newDep, err := dbInstance.GenDeployment()
		if err != nil {
			continue
		}

		err = createK8sInstanceOneApplyDeployment(c, kSet, *dbInstance, newDep)
		if err != nil {

			msg := fmt.Sprintf("更新app遍历instance创建Deployment或更新错误:%v", err.Error())
			sc.Logger.Error(msg, zap.Any("k8s集群配置", searchCluster), zap.Error(err))
			common.FailWithMessage(msg, c)
			return
		}

	}

	common.OkWithData(reqObj, c)
	//common.OkWithMessage("更新成功", c)
}

func deleteK8sAppOne(c *gin.Context) {
	sc := c.MustGet(common.GIN_CTX_CONFIG_CONFIG).(*config.ServerConfig)
	// 校验一下 menu字段
	id := c.Param("id")

	// 先 去db中根据id找到这个对象
	intVar, _ := strconv.Atoi(id)
	dbObj, err := models.GetK8sAppById(intVar)
	if err != nil {
		sc.Logger.Error("根据id找k8s-app配置错误", zap.Any("k8s-app配置", id), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}

	dbObj.FillFrontAllData()
	//在创建节点前要校验权限
	pass, _ := streeNodeOpsAdminPermissionCheck(dbObj.StreeNodeObj, c)
	if !pass {
		sc.Logger.Error("服务树节点权限校验未通过", zap.Any("reqNode", dbObj.TreeNodeId), zap.Error(err))
		common.Req403WithWithMessage("服务树节点权限校验未通过", c)
		return
	}
	//到这里

	//  做个保护，查询一下template 中有没有用到 这个id的
	dbInstances, _ := models.GetK8sInstanceByK8sAppId(intVar)
	if len(dbInstances) > 0 {
		msg := "根据k8s-app-id找到了实例 禁止删除"
		sc.Logger.Error("", zap.Any("k8s-app配置", id), zap.Error(err))
		common.FailWithMessage(msg, c)
		return
	}

	// 删除svc就可以了
	// 拿到k8s集群对象

	searchCluster := dbObj.ClusterObj.Name

	// 从cache中通过 集群的id 拿到集群的clientSet
	kc := c.MustGet(common.GIN_CTX_K8s_CACHE).(*cache.K8sClusterCache)
	kSet := kc.GetClusterClientSetById(dbObj.ClusterObj.ID)
	if kSet == nil {
		msg := fmt.Sprintf("根据id找k8s集群kset缓存错误")
		sc.Logger.Error(msg, zap.Any("k8s集群配置", searchCluster), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}

	ctx1, canFun1 := common.GenTimeoutContext(dbObj.ClusterObj.ActionTimeoutSeconds)
	defer canFun1()
	err = kSet.CoreV1().Services(dbObj.Namespace).Delete(ctx1, dbObj.Name, metav1.DeleteOptions{})
	if err != nil {
		msg := fmt.Sprintf("删除集群中的svc错误:%v", dbObj.Name)
		sc.Logger.Error(msg, zap.Any("k8s集群配置", searchCluster), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}

	err = dbObj.DeleteOne()
	if err != nil {
		sc.Logger.Error("根据id删除k8s-app数据库错误", zap.Any("k8s-app", id), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}
	common.OkWithMessage("删除成功", c)
}

func getK8sAppOne(c *gin.Context) {
	sc := c.MustGet(common.GIN_CTX_CONFIG_CONFIG).(*config.ServerConfig)
	// 校验一下 menu字段
	id := c.Param("id")

	// 先 去db中根据id找到这个user
	intVar, _ := strconv.Atoi(id)
	dbObj, err := models.GetK8sAppById(intVar)
	if err != nil {
		sc.Logger.Error("根据id找k8s应用错误", zap.Any("任务实例", id), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}
	dbObj.FillFrontAllData()

	common.OkWithData(dbObj, c)
}

// 传入集群和app的名称返回它的id
func getK8sAppOneByClusterAndName(c *gin.Context) {
	sc := c.MustGet(common.GIN_CTX_CONFIG_CONFIG).(*config.ServerConfig)
	searchName := c.DefaultQuery("name", "")

	// 查询条件
	searchCluster := c.DefaultQuery("cluster", "")

	// 先 去db中根据id找到这个user
	dbObj, err := models.GetK8sAppByClusterAndName(searchCluster, searchName)
	if err != nil {
		sc.Logger.Error("根据id找k8s应用错误", zap.Any("searchName", searchName), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}
	c.String(200, fmt.Sprintf("%v", dbObj.ID))
	//dbObj.FillFrontAllData()
	//c.Writer
	//common.OkWithData(dbObj, c)
}

func getK8sAppListForSelect(c *gin.Context) {
	sc := c.MustGet(common.GIN_CTX_CONFIG_CONFIG).(*config.ServerConfig)

	objs, err := models.GetK8sAppAll()
	if err != nil {
		sc.Logger.Error("去数据库中拿所有的k8s项目配置错误",
			zap.Error(err),
		)
		common.ReqBadFailWithMessage(fmt.Sprintf("去数据库中拿所有的k8s项目配置错误:%v", err.Error()), c)
		return
	}
	res := []common.CommonSelectOneItemValueInt{}
	for _, obj := range objs {
		obj := obj
		res = append(res, common.CommonSelectOneItemValueInt{
			Label: fmt.Sprintf("集群:%v 应用:%v", obj.Cluster, obj.Name),
			Value: int(obj.ID),
		})
	}

	common.OkWithDetailed(res, "ok", c)

}
